package wumpusworld;

public class KnowledgeMap 
{	
	World myWorld;
	Square[][] myMap = new Square[4][4];
	
	public KnowledgeMap (World w)
	{
		myWorld = w;
		for (int i = 0; i < 4 ;i++)
		{
			for (int j =0; j < 4; j++)
			{	
				if ((i == 0 && j == 0) || (i==0 && j==3) || (i==3 && j==0) || (i==3 && j==3))
				{
					myMap[i][j] = new Square(2);
				}
				else 
				{
					if (i == 0 || i == 3 || j == 0 || j ==3)
					{
						myMap[i][j] = new Square(3);
					}
					else 
					{
						myMap[i][j] = new Square(4);
					}
				}	
			}
		}
	}
	
	// have to be call each time the agent move to compute the new information
	
	public void computePosition (int x, int y)
	{	
		if (!myWorld.wumpusAlive())
		{
			clearWumpus();
		}
		if (myWorld.isVisited(x, y) && myMap[x-1][y-1].isFirstTime())
		{
			myMap[x-1][y-1].setFirstTime(false);
			if (myWorld.hasPit(x, y))
			{
				myMap[x-1][y-1].setPit(true);
			}
			if (myMap[x-1][y-1].isPossiblyWumpus() && !myWorld.hasWumpus(x, y))
			{
				myMap[x-1][y-1].noWumpus();
			}
			boolean stench = myWorld.hasStench(x, y);
			boolean breeze = myWorld.hasBreeze(x, y);
			if (stench && breeze)
			{
					computeStenchBreeze(x-1, y-1);
			}
			else 
			{
				if (stench)
				{
					computeStench (x-1, y-1);
				}
				else
					{
					if(breeze)
					{
						computeBreeze (x-1,y-1);
					}
					else
					{
						setEmpty(x-1, y-1);
						computeSafeInformation(x-1, y-1);
					}
				}
			}
		}
	}
	
	
	/*****Getter and Setter******/
	
	public void setEmpty(int x, int y)
	{
		if (myWorld.isValidPosition(x+1, y+2))
		{
			setSafe(x, y+1); 
		}
		
		if (myWorld.isValidPosition(x+2, y+1))
		{
			setSafe(x+1, y); 
		}
		
		if (myWorld.isValidPosition(x, y+1))
		{
			setSafe(x-1, y); 
		}
		
		if (myWorld.isValidPosition(x+1, y))
		{
			setSafe(x, y-1); 
		}
	}
	
	public boolean hasWumpus(int x, int y) 
	{
		return myMap[x-1][y-1].isWumpus();
	}

	public boolean hasPossiblyWumpus(int x, int y) 
	{
		return myMap[x-1][y-1].isPossiblyWumpus();
	}

	public boolean hasPit(int x, int y) 
	{
		return myMap[x-1][y-1].isPit();
	}

	public boolean hassPossiblyPit(int x, int y) 
	{
		return myMap[x-1][y-1].isPossiblyPit();
	}
	
	public String getSample()
	{	

		if (myMap[2][1].isWumpus())
		{
			return "WUMPUS";
		}
		if (myMap[2][1].isSafe())
		{
			return "safe";
		}
		return "stenhCount :" + myMap[2][2].getStenchCount() + "  Breeze Count :" +myMap[2][2].getBreezeCount();
	}

	public boolean isSafe(int x, int y) 
	{
		return myMap[x-1][y-1].isSafe();
	}

	private void setSafe(int x, int y) 
	{
		myMap[x][y].setSafe();
	}
	
	/********** computing method **************/
	
	private void computeStenchBreeze(int x, int y)
	{	
		//System.out.println("Stench + Breeze");
		if (myWorld.isValidPosition(x+1, y+2) && !myMap[x][y+1].isSafe() )
		{
			if(myMap[x][y+1].isAdjacent())
			{
				if (myMap[x][y+1].isPossiblyWumpus())
				{
					myMap[x][y+1].setPossiblyWumpus(true);
				}
				if (myMap[x][y+1].isPossiblyPit())
				{
					myMap[x][y+1].setPossiblyPit(true);
				}
			}
			else
			{
				myMap[x][y+1].setPossiblyWumpus(true);
				myMap[x][y+1].setPossiblyPit(true);
			}
		}
		
		if (myWorld.isValidPosition(x+2, y+1) && !myMap[x+1][y].isSafe())
		{
			if(myMap[x+1][y].isAdjacent())
			{
				if (myMap[x+1][y].isPossiblyWumpus())
				{
					myMap[x+1][y].setPossiblyWumpus(true);
				}
				if (myMap[x+1][y].isPossiblyPit())
				{
					myMap[x+1][y].setPossiblyPit(true);
				}
			}
			else
			{
				myMap[x+1][y].setPossiblyWumpus(true);
				myMap[x+1][y].setPossiblyPit(true);
			}
		}
		
		if (myWorld.isValidPosition(x, y+1) && !myMap[x-1][y].isSafe())
		{
			if(myMap[x-1][y].isAdjacent())
			{
				if (myMap[x-1][y].isPossiblyWumpus())
				{
					myMap[x-1][y].setPossiblyWumpus(true);
				}
				if (myMap[x-1][y].isPossiblyPit())
				{
					myMap[x-1][y].setPossiblyPit(true);
				}
			}
			else
			{
				myMap[x-1][y].setPossiblyWumpus(true);
				myMap[x-1][y].setPossiblyPit(true);
			}
		}
		
		if (myWorld.isValidPosition(x+1, y) && !myMap[x][y-1].isSafe())
		{
			if(myMap[x][y-1].isAdjacent())
			{
				if (myMap[x][y-1].isPossiblyWumpus())
				{
					myMap[x][y-1].setPossiblyWumpus(true);
				}
				if (myMap[x][y-1].isPossiblyPit())
				{
					myMap[x][y-1].setPossiblyPit(true);
				}
			}
			else
			{
				myMap[x][y-1].setPossiblyWumpus(true);
				myMap[x][y-1].setPossiblyPit(true);
			}
		}
	}
	
	private void computeStench(int x, int y)
	{	
		if (myWorld.isValidPosition(x+1, y+2) && !myMap[x][y+1].isSafe() )
		{	
			if (myMap[x][y+1].isPossiblyPit() && !myMap[x][y+1].isPossiblyWumpus())
			{
				myMap[x][y+1].setSafe();
				computeSafeInformation(x, y+1);
			}
			else
			{
				myMap[x][y+1].setPossiblyWumpus(true);
			} 
		}
		
		if (myWorld.isValidPosition(x+2, y+1) && !myMap[x+1][y].isSafe())
		{
			if (myMap[x+1][y].isPossiblyPit() && !myMap[x+1][y].isPossiblyWumpus())
			{
				myMap[x+1][y].setSafe();
				computeSafeInformation(x+1, y);
			}
			else
			{
				myMap[x+1][y].setPossiblyWumpus(true);
			} 
		}

		if (myWorld.isValidPosition(x, y+1) && !myMap[x-1][y].isSafe())
		{
			if (myMap[x-1][y].isPossiblyPit() && !myMap[x-1][y].isPossiblyWumpus())
			{
				myMap[x-1][y].setSafe();
				computeSafeInformation(x-1, y);
			}
			else
			{
				myMap[x-1][y].setPossiblyWumpus(true);
			} 
		}
		
		if (myWorld.isValidPosition(x+1, y) && !myMap[x][y-1].isSafe())
		{
			if (myMap[x][y-1].isPossiblyPit() && !myMap[x][y-1].isPossiblyWumpus())
			{
				myMap[x][y-1].setSafe();
				computeSafeInformation(x, y-1);
			}
			else
			{
				myMap[x][y-1].setPossiblyWumpus(true);
			} 
		}
		
		computeNeighbourWumpus(x, y);
	}
	
	private void computeBreeze (int x, int y)
	{
		if (myWorld.isValidPosition(x+1, y+2) && !myMap[x][y+1].isSafe() )
		{	
			if (!myMap[x][y+1].isPossiblyPit() && myMap[x][y+1].isPossiblyWumpus())
			{
				myMap[x][y+1].setSafe();
				computeSafeInformation(x, y+1);
			}
			else
			{
				myMap[x][y+1].setPossiblyPit(true);
			} 
		}
		
		if (myWorld.isValidPosition(x+2, y+1) && !myMap[x+1][y].isSafe())
		{
			if (!myMap[x+1][y].isPossiblyPit() && myMap[x+1][y].isPossiblyWumpus())
			{
				myMap[x+1][y].setSafe();
				computeSafeInformation(x+1, y);
			}
			else
			{
				myMap[x+1][y].setPossiblyPit(true);
			} 
		}
		
		if (myWorld.isValidPosition(x, y+1) && !myMap[x-1][y].isSafe())
		{
			if (!myMap[x-1][y].isPossiblyPit() && myMap[x-1][y].isPossiblyWumpus())
			{
				myMap[x-1][y].setSafe();
				computeSafeInformation(x-1, y);
			}
			else
			{
				myMap[x-1][y].setPossiblyPit(true);
			} 
		}
		
		if (myWorld.isValidPosition(x+1, y) && !myMap[x][y-1].isSafe())
		{
			if (!myMap[x][y-1].isPossiblyPit() && myMap[x][y-1].isPossiblyWumpus())
			{
				myMap[x][y-1].setSafe();
				computeSafeInformation(x, y-1);
			}
			else
			{
				myMap[x][y-1].setPossiblyPit(true);
			} 
		}
		computeNeighbourPit(x, y);
	}
	
	private void computeNeighbourWumpus (int x, int y)
	{	
		int safeCount =0;
		
		if (myWorld.isValidPosition(x+1, y+2) && myMap[x][y+1].isSafe() )
		{	
				safeCount++;
		}
		
		if (myWorld.isValidPosition(x+2, y+1) && myMap[x+1][y].isSafe())
		{
				safeCount++; 
		}
		
		if (myWorld.isValidPosition(x, y+1) && myMap[x-1][y].isSafe())
		{
				safeCount++;
		}
		
		if (myWorld.isValidPosition(x+1, y) && myMap[x][y-1].isSafe())
		{
				safeCount++;
		}
		
		if (safeCount >= myMap[x][y].getNgbNbr()-1)
		{
			if (myWorld.isValidPosition(x+1, y+2) && !myMap[x][y+1].isSafe() )
			{	
					myMap[x][y+1].setWumpus(true);
					wumpusFound();
			}
			
			if (myWorld.isValidPosition(x+2, y+1) && !myMap[x+1][y].isSafe())
			{
					myMap[x+1][y].setWumpus(true);
					wumpusFound();
			}
			
			if (myWorld.isValidPosition(x, y+1) && !myMap[x-1][y].isSafe())
			{
					myMap[x-1][y].setWumpus(true);
					wumpusFound();
			}
			
			if (myWorld.isValidPosition(x+1, y) && !myMap[x][y-1].isSafe())
			{
					myMap[x][y-1].setWumpus(true);
					wumpusFound();
			}
		}
	}

	private void computeNeighbourPit (int x, int y)
	{	
		int safeCount =0;
		
		if (myWorld.isValidPosition(x+1, y+2) && myMap[x][y+1].isSafe() )
		{	
				safeCount++;
		}
		
		if (myWorld.isValidPosition(x+2, y+1) && myMap[x+1][y].isSafe())
		{
				safeCount++; 
		}
		
		if (myWorld.isValidPosition(x, y+1) && myMap[x-1][y].isSafe())
		{
				safeCount++;
		}
		
		if (myWorld.isValidPosition(x+1, y) && myMap[x][y-1].isSafe())
		{
				safeCount++;
		}
		
		if (safeCount >= myMap[x][y].getNgbNbr()-1)
		{
			if (myWorld.isValidPosition(x+1, y+2) && !myMap[x][y+1].isSafe() )
			{	
					myMap[x][y+1].setPit(true);
			}
			
			if (myWorld.isValidPosition(x+2, y+1) && !myMap[x+1][y].isSafe())
			{
					myMap[x+1][y].setPit(true); 
			}
			
			if (myWorld.isValidPosition(x, y+1) && !myMap[x-1][y].isSafe())
			{
					myMap[x-1][y].setPit(true);
			}
			
			if (myWorld.isValidPosition(x+1, y) && !myMap[x][y-1].isSafe())
			{
					myMap[x][y-1].setPit(true);
			}
		}
	}
	
	private void computeSafeInformation (int x, int y)
	{
		if (myWorld.isValidPosition(x+1, y+2) && myWorld.isVisited(x+1, y+2) )
		{
			if (myWorld.hasStench(x+1, y+2))
			{
				computeNeighbourWumpus(x, y+1);
			}
			if (myWorld.hasBreeze(x+1, y+2))
			{
				computeNeighbourPit(x+1, y);
			} 
		}
		
		if (myWorld.isValidPosition(x+2, y+1) && myWorld.isVisited(x+2, y+1))
		{
			if (myWorld.hasStench(x+2, y+1))
			{
				computeNeighbourWumpus(x, y+1);
			}
			if (myWorld.hasBreeze(x+2, y+1))
			{
				computeNeighbourPit(x+1, y);
			} 
		}
		
		if (myWorld.isValidPosition(x, y+1) && myWorld.isVisited(x, y+1))
		{
			if (myWorld.hasStench(x, y+1))
			{
				computeNeighbourWumpus(x-1, y);
			}
			if (myWorld.hasBreeze(x, y+1))
			{
				computeNeighbourPit(x-1, y);
			}
		}
		
		if (myWorld.isValidPosition(x+1, y) && myWorld.isVisited(x+1, y))
		{
			if (myWorld.hasStench(x+1, y))
			{
				computeNeighbourWumpus(x, y-1);
			}
			if (myWorld.hasBreeze(x+1, y))
			{
				computeNeighbourPit(x, y-1);
			}
		}
	}

	public int[] getNextTarget (int x, int y)
	{
		// check first if there is a safe place to go
		for (int i = 1; i < 4; i++)
		{	
			if (myWorld.isValidPosition(x+i, y) && !myWorld.isVisited(x+i, y) && myMap[x-1+i][y-1].isSafe())
			{
				return new int[]{x+i, y};
			}
			
			if (myWorld.isValidPosition(x-i, y) && !myWorld.isVisited(x-i, y) && myMap[x-1-i][y-1].isSafe())
			{
				return new int[]{x-i, y};
			}
			
			if (myWorld.isValidPosition(x, y+i) && !myWorld.isVisited(x, y+i) && myMap[x-1][y-1+i].isSafe())
			{
				return new int[]{x, y+i};
			}
			
			if (myWorld.isValidPosition(x, y-i) && !myWorld.isVisited(x, y-i) && myMap[x-1][y-1-i].isSafe())
			{
				return new int[]{x, y-i};
			}
			
			for (int j = 1; j < i+1; j++)
			{
				if (myWorld.isValidPosition(x+i, y+j) && !myWorld.isVisited(x+i, y+j) && myMap[x-1+i][y-1+j].isSafe())
				{
					return new int[]{x+i, y+j};
				}
				
				if (myWorld.isValidPosition(x+i, y-j) && !myWorld.isVisited(x+i, y-j) && myMap[x-1+i][y-1-j].isSafe())
				{
					return new int[]{x+i, y-j};
				}
				
				if (myWorld.isValidPosition(x-i, y+j) && !myWorld.isVisited(x-i, y+j) && myMap[x-1-i][y-1+j].isSafe())
				{
					return new int[]{x-i, y+j};
				}
				
				if (myWorld.isValidPosition(x-i, y-j) && !myWorld.isVisited(x-i, y-j) && myMap[x-1-i][y-1-j].isSafe())
				{
					return new int[]{x-i, y-j};
				}
				
				if (myWorld.isValidPosition(x+j, y+i) && !myWorld.isVisited(x+j, y+i) && myMap[x-1+j][y-1+i].isSafe())
				{
					return new int[]{x+j, y+i};
				}
				
				if (myWorld.isValidPosition(x-j, y+i) && !myWorld.isVisited(x-j, y+i) && myMap[x-1-j][y-1+i].isSafe())
				{
					return new int[]{x-j, y+i};
				}
				
				if (myWorld.isValidPosition(x+j, y-i) && !myWorld.isVisited(x+j, y-i) && myMap[x-1+j][y-1-i].isSafe())
				{
					return new int[]{x+j, y-i};
				}
				
				if (myWorld.isValidPosition(x-j, y-i) && !myWorld.isVisited(x-j, y-i) && myMap[x-1-j][y-1-i].isSafe())
				{
					return new int[]{x-j, y-i};
				}
			}
		}
		
		// if there is no place to go, check if there is a place with no possibly wumpus to go;
		
		for (int i = 1; i < 4; i++)
		{	
			if (myWorld.isValidPosition(x+i, y) && !myWorld.isVisited(x+i, y) && !myMap[x-1+i][y-1].isPossiblyWumpus() && myMap[x-1+i][y-1].isAdjacent())
			{
				return new int[]{x+i, y};
			}
			
			if (myWorld.isValidPosition(x-i, y) && !myWorld.isVisited(x-i, y) && !myMap[x-1-i][y-1].isPossiblyWumpus() && myMap[x-1-i][y-1].isAdjacent())
			{
				return new int[]{x-i, y};
			}
			
			if (myWorld.isValidPosition(x, y+i) && !myWorld.isVisited(x, y+i) && !myMap[x-1][y-1+i].isPossiblyWumpus() && myMap[x-1][y-1+i].isAdjacent())
			{
				return new int[]{x, y+i};
			}
			
			if (myWorld.isValidPosition(x, y-i) && !myWorld.isVisited(x, y-i) && !myMap[x-1][y-1-i].isPossiblyWumpus() && myMap[x-1][y-1-i].isAdjacent())
			{
				return new int[]{x, y-i};
			}
			
			for (int j = 1; j < i+1; j++)
			{
				if (myWorld.isValidPosition(x+i, y+j) && !myWorld.isVisited(x+i, y+j) && !myMap[x-1+i][y-1+j].isPossiblyWumpus() && myMap[x-1+i][y-1+j].isAdjacent())
				{
					return new int[]{x+i, y+j};
				}
				
				if (myWorld.isValidPosition(x+i, y-j) && !myWorld.isVisited(x+i, y-j) && !myMap[x-1+i][y-1-j].isPossiblyWumpus() && myMap[x-1+i][y-1-j].isAdjacent())
				{
					return new int[]{x+i, y-j};
				}
				
				if (myWorld.isValidPosition(x-i, y+j) && !myWorld.isVisited(x-i, y+j) && !myMap[x-1-i][y-1+j].isPossiblyWumpus() && myMap[x-1-i][y-1+j].isAdjacent())
				{
					return new int[]{x-i, y+j};
				}
				
				if (myWorld.isValidPosition(x-i, y-j) && !myWorld.isVisited(x-i, y-j) && !myMap[x-1-i][y-1-j].isPossiblyWumpus() && myMap[x-1-i][y-1-j].isAdjacent())
				{
					return new int[]{x-i, y-j};
				}
				
				if (myWorld.isValidPosition(x+j, y+i) && !myWorld.isVisited(x+j, y+i) && !myMap[x-1+j][y-1+i].isPossiblyWumpus() && myMap[x-1+j][y-1+i].isAdjacent())
				{
					return new int[]{x+j, y+i};
				}
				
				if (myWorld.isValidPosition(x-j, y+i) && !myWorld.isVisited(x-j, y+i) && !myMap[x-1-j][y-1+i].isPossiblyWumpus() && myMap[x-1-j][y-1+i].isAdjacent())
				{
					return new int[]{x-j, y+i};
				}
				
				if (myWorld.isValidPosition(x+j, y-i) && !myWorld.isVisited(x+j, y-i) && !myMap[x-1+j][y-1-i].isPossiblyWumpus() && myMap[x-1+j][y-1-i].isAdjacent())
				{
					return new int[]{x+j, y-i};
				}
				
				if (myWorld.isValidPosition(x-j, y-i) && !myWorld.isVisited(x-j, y-i) && !myMap[x-1-j][y-1-i].isPossiblyWumpus() && myMap[x-1-j][y-1-i].isAdjacent())
				{
					return new int[]{x-j, y-i};
				}
			}
		}
	return new int[]{0, 0};
	}

	public char shouldIShoot(int x, int y)
	{
		if (myWorld.isValidPosition(x+1, y) &&  myMap[x][y-1].isWumpus())
		{
			wumpusFound();
			return 'r';
		}
		
		
		if (myWorld.isValidPosition(x, y+1) && myMap[x-1][y].isWumpus())
		{
			wumpusFound();
			return 't';
		}
		
		if (myWorld.isValidPosition(x, y-1) && myMap[x-1][y-2].isWumpus())
		{
			wumpusFound();
			return 'b';
		}
		
		if (myWorld.isValidPosition(x-1, y) && myMap[x-2][y-1].isWumpus())
		{
			wumpusFound();
			return 'l';
		}
		
		return 'n';
	}
	
	public void wumpusFound()
	{
		for (int i = 0; i<4; i++)
		{
			for (int j = 0; j< 4; j++)
			{
				if (myMap[i][j].isPossiblyWumpus() && !myMap[i][j].isWumpus())
				{
					if (myMap[i][j].isPossiblyPit())
					{
						myMap[i][j].setPossiblyWumpus(false);
					}
					else
					{
						myMap[i][j].setSafe();
					}
				}
			}
		}
	}
	
	public void clearWumpus()
	{
		for (int i = 0; i<4; i++)
		{
			for (int j = 0; j< 4; j++)
			{
				if (myMap[i][j].isWumpus())
				{
					if (myMap[i][j].isPossiblyPit())
					{
						myMap[i][j].setPossiblyWumpus(false);
						myMap[i][j].setWumpus(false);
					}
					else
					{
						myMap[i][j].setSafe();
					}
				}
			}
		}
	}
}
